package com.autumn.platform.web.bind.provider.impl;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.servlet.ServletRequest;

import org.springframework.beans.PropertyAccessor;

import com.autumn.platform.core.Utils;
import com.autumn.platform.core.context.user.ISessionUser;
import com.autumn.platform.web.WebHolder;
import com.autumn.platform.web.annotation.User;
import com.autumn.platform.web.bind.RelaxedBeanWrapper;
import com.autumn.platform.web.bind.provider.IPropertyValuesProvider;

public class SessionUserPropertyValuesProvider<U extends ISessionUser> implements IPropertyValuesProvider {

    private Map<Class<?>, List<FieldUserProperty>> cache = new ConcurrentHashMap<Class<?>, List<FieldUserProperty>>();

    @Override
    public void afterBindValues(PropertyAccessor accessor, ServletRequest request, Object target, String name) {
        @SuppressWarnings("unchecked")
        U sessionUser = (U) WebHolder.getSessionUser();
        if (null == sessionUser) {
            return;
        }
        PropertyAccessor upa = new RelaxedBeanWrapper(sessionUser);
        for (Class<?> cls = target.getClass(); !cls.equals(Object.class); cls = cls.getSuperclass()) {
            List<FieldUserProperty> fieldAnnos = resolveFieldAnnos(cls);
            if (null != fieldAnnos && !fieldAnnos.isEmpty()) {
                for (FieldUserProperty fieldAnno : fieldAnnos) {
                    accessor.setPropertyValue(fieldAnno.field, getUserPropertyValue(upa, sessionUser, fieldAnno.userProperty));
                }
            }
        }
    }

    protected Object getUserPropertyValue(PropertyAccessor accessor, U user, String userProperty) {
        if ("this".equals(userProperty)) {
            return user;
        }
        return accessor.getPropertyValue(userProperty);
    }

    protected String getUserProperty(Field field) {
        User user = field.getAnnotation(User.class);
        if (null != user) {
            return Utils.isBlank(user.value()) ? "userId" : user.value();
        }
        return null;
    }

    private List<FieldUserProperty> resolveFieldAnnos(Class<?> cls) {
        List<FieldUserProperty> fieldAnnos = cache.get(cls);
        if (null == fieldAnnos) {
            fieldAnnos = new ArrayList<FieldUserProperty>();
            Field[] fields = cls.getDeclaredFields();
            if (null != fields) {
                for (Field field : fields) {
                    String userProperty = this.getUserProperty(field);
                    if (!Utils.isBlank(userProperty)) {
                        fieldAnnos.add(new FieldUserProperty(field.getName(), userProperty));
                    }
                }
            }
            if (fieldAnnos.isEmpty()) {
                fieldAnnos = Collections.emptyList();
            }
            cache.put(cls, fieldAnnos);
        }
        return fieldAnnos;
    }

    private static class FieldUserProperty {
        private String field;
        private String userProperty;

        public FieldUserProperty(String field, String userProperty) {
            this.field = field;
            this.userProperty = userProperty;
        }
    }

}
